package cn.ywang.spring.customized.framework.webmvc.servlet;

import cn.ywang.spring.customized.framework.annotation.Controller;
import cn.ywang.spring.customized.framework.annotation.RequestMapping;
import cn.ywang.spring.customized.framework.annotation.RequestParam;
import cn.ywang.spring.customized.framework.aop.AopProxyUtils;
import cn.ywang.spring.customized.framework.context.ApplicationContext;
import cn.ywang.spring.customized.framework.webmvc.HandlerAdapter;
import cn.ywang.spring.customized.framework.webmvc.HandlerMapping;
import cn.ywang.spring.customized.framework.webmvc.ModelAndView;
import cn.ywang.spring.customized.framework.webmvc.ViewResolver;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author BeatificWang
 * @date 2018/11/3 22:49
 */
public class DispatchServlet extends HttpServlet {

	/**
	 * application.properties文件地址
	 */
	private static final String LOCATION = "contextConfigLocation";

	private static final String TEMPLATE_LOCATION = "templateRoot";

	private Properties contextConfig = new Properties();

	private Map<String, Object> beanMap = new ConcurrentHashMap<>();

	private List<String> classNames = new ArrayList<>();

	// URL与Method映射信息
	private List<HandlerMapping> handlerMappings = new ArrayList<>();

	private Map<HandlerMapping, HandlerAdapter> handlerAdapterMap = new HashMap<>();

	private List<ViewResolver> viewResolvers = new ArrayList<>();


	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("******调用POST请求******");
		try {
			this.doDispatch(req, resp);
		} catch (Exception e) {
			resp.getWriter().write("<font size='25' color='blue'>500 Exception</font><br/>Details:<br/>" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", ""));
			e.printStackTrace();
		}
	}

	/**
	 * 转发请求
	 *
	 * @param req
	 * @param resp
	 * @throws Exception
	 */
	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		// 根据用户请求的URL来获得一个handler
		HandlerMapping handlerMapping = this.getHandler(req);
		if (handlerMapping == null) {
			resp.getWriter().write("<font size='25' color='red'>404 Not Found</font><br/><font color='green'><i>Copyright@GupaoEDU</i></font>");
			return;
		}
		HandlerAdapter ha = this.getHandlerAdapter(handlerMapping);
		ModelAndView mv = ha.handle(req, resp, handlerMapping);
		this.processDispatchResult(resp, mv);
	}

	private void processDispatchResult(HttpServletResponse resp, ModelAndView mv) throws Exception {
		//调用viewResolver的resolveView方法
		if (mv == null) {
			return;
		}
		if (this.viewResolvers.isEmpty()) {
			return;
		}
		for (ViewResolver vr : this.viewResolvers) {
			if (mv.getViewName().equals(vr.getViewName())) {
				String out = vr.resolveViewName(mv);
				if (out != null) {
					try {
						resp.getWriter().println(out);
					} catch (IOException e) {
						e.printStackTrace();
					}
					break;
				}
			}
		}
	}

	private HandlerMapping getHandler(HttpServletRequest req) {
		// uri资源地址
		String url = req.getRequestURI();
		// 全局地址
		String contextPath = req.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		for (HandlerMapping handlerMapping : this.handlerMappings) {
			Matcher matcher = handlerMapping.getPattern().matcher(url);
			if (!matcher.matches()) {
				continue;
			}
			return handlerMapping;
		}
		return null;
	}

	private HandlerAdapter getHandlerAdapter(HandlerMapping handlerMapping) {
		return this.handlerAdapterMap.get(handlerMapping);
	}

	@Override
	public void init(ServletConfig config) throws ServletException {

		// IOC容器初始化，已经预加载了
		ApplicationContext context = new ApplicationContext(config.getInitParameter(LOCATION));
		//		MyAction myAction = (MyAction) context.getBean("myAction");
		//		myAction.query("wang");

		this.initStrategies(context);
	}

	/**
	 * 初始化策略
	 * 这边手动实现 initHandlerMappings initHandlerAdapters initViewResolvers
	 *
	 * @param context
	 */
	protected void initStrategies(ApplicationContext context) {
		// 上传文件解析
		this.initMultipartResolver(context);
		// 本地化解析
		this.initLocaleResolver(context);
		// 主题解析
		this.initThemeResolver(context);
		// 将请求映射到处理器
		//用来保存Controller中配置的RequestMapping和Method的一个对应关系
		this.initHandlerMappings(context);
		// 根据参数类型动态匹配
		// 用来动态匹配Method参数，包括类转换，动态赋值
		this.initHandlerAdapters(context);
		// 异常处理
		this.initHandlerExceptionResolvers(context);
		// 解析请求到视图名
		this.initRequestToViewNameTranslator(context);
		// 解析逻辑视图到具体视图实现
		// 实现动态模板的解析
		this.initViewResolvers(context);
		// flash映射管理器
		this.initFlashMapManager(context);
	}

	/**
	 * 初始化URL与方法映射关系
	 *
	 * @param context
	 */
	private void initHandlerMappings(ApplicationContext context) {
		// 从容器中获取所有的BeanName
		String[] beanNames = context.getBeanNames();
		try {
			for (String beanName : beanNames) {
				// MVC层对外提供的只要getBean
				// 若返回的不是BeanWrapper怎么办？
				// 返回包装后的bean
				Object proxy = context.getBean(beanName);
				// 被代理类
				Object controller = AopProxyUtils.getTargetObject(proxy);
				Class<?> clazz = controller.getClass();
				if (clazz == null) {
					continue;
				}
				if (!clazz.isAnnotationPresent(Controller.class)) {
					continue;
				}
				// 类级别的@RequestMapping注解的URI地址
				String baseUrl = "";
				if (clazz.isAnnotationPresent(RequestMapping.class)) {
					RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
					baseUrl = requestMapping.value();
				}
				// 扫描所有的public方法
				Method[] methods = clazz.getMethods();
				for (Method method : methods) {
					if (!method.isAnnotationPresent(RequestMapping.class)) {
						continue;
					}
					RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
					// 方法级别的@RequestMapping注解的URI地址
					String methodUrl = requestMapping.value();
					String regex = ("/" + baseUrl + methodUrl.replaceAll("\\*", ".*")).replaceAll("/+", "/");
					Pattern pattern = Pattern.compile(regex);
					this.handlerMappings.add(new HandlerMapping(pattern, controller, method));
					System.out.println("Mapping: " + regex + "," + method);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void initHandlerAdapters(ApplicationContext context) {
		// 在初始化阶段，将这些参数的名字或者顺序按照一定的顺序保存下来
		// 因为后续反射调用的时候，形参是一个数组
		// 可以通过记录这些参数的位置的index，挨个从数组中取值，这样就和参数的顺序无关了
		for (HandlerMapping handlerMapping : this.handlerMappings) {
			// 每一个方法有一个参数列表，这里保存的是形参列表
			Map<String, Integer> paramMapping = new HashMap<>();

			// 处理命名参数
			Annotation[][] pa = handlerMapping.getMethod().getParameterAnnotations();
			for (int i = 0; i < pa.length; i++) {
				for (Annotation annotation : pa[i]) {
					if (annotation instanceof RequestParam) {
						String paramName = ((RequestParam) annotation).value();
						if (!StringUtils.isBlank(paramName)) {
							paramMapping.put(paramName, i);
						}
					}
				}
			}
			// 处理未命名参数
			//			Method method = handlerMapping.getMethod();
			//			Parameter[] parameters = method.getParameters();
			//			for (Parameter parameter : parameters) {
			//				String paramName = parameter.getName();
			//				if (paramMapping.containsKey(paramName)) {
			//					continue;
			//				}
			//			}
			// 处理 request response
			Class<?>[] paramTypes = handlerMapping.getMethod().getParameterTypes();
			for (int i = 0; i < paramTypes.length; i++) {
				Class<?> type = paramTypes[i];
				if ((type == HttpServletRequest.class) || type == HttpServletResponse.class) {
					paramMapping.put(type.getName(), i);
				}
			}
			this.handlerAdapterMap.put(handlerMapping, new HandlerAdapter(paramMapping));

		}
	}

	private void initViewResolvers(ApplicationContext context) {
		//在页面敲一个 http://localhost/first.html
		//解决页面名字和模板文件关联的问题
		String templateRoot = context.getConfig().getProperty(TEMPLATE_LOCATION);
		String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

		File templateRootDir = new File(templateRootPath);
		for (File template : templateRootDir.listFiles()) {
			this.viewResolvers.add(new ViewResolver(template.getName(), template));
		}

	}

	private void initMultipartResolver(ApplicationContext context) {
	}

	private void initLocaleResolver(ApplicationContext context) {
	}

	private void initThemeResolver(ApplicationContext context) {
	}

	private void initHandlerExceptionResolvers(ApplicationContext context) {
	}

	private void initRequestToViewNameTranslator(ApplicationContext context) {
	}

	private void initFlashMapManager(ApplicationContext context) {

	}

}
